#include "UnitMesh.h"
#include "D3DHelper.h"

const string ModelsDir = "..\\Models\\";

const D3D11_INPUT_ELEMENT_DESC Vertex::layout[] = {
	{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
	{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT , D3D11_INPUT_PER_VERTEX_DATA, 0 },
	{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0}
}; 


bool CUnitMesh::InitFromScene( const aiScene* pScene, const std::string& Filename )
{
	m_Entries.resize(pScene->mNumMeshes);

	// Initialize the meshes in the scene one by one
	for (unsigned int i = 0 ; i < m_Entries.size() ; i++) 
	{
		const aiMesh* paiMesh = pScene->mMeshes[i];
		InitMesh(i, paiMesh);
	}

	InitMetrials(pScene, Filename);
	return InitD3DObjects();
}

void CUnitMesh::InitMesh(unsigned int Index, const aiMesh* paiMesh)
{
	UINT nIndices = 0;
	m_Entries[Index].StartIndexLocation = m_Indices.size();
	m_Entries[Index].BaseVertex = m_Vertices.size();

	const aiVector3D Zero3D(0.0f, 0.0f, 0.0f);

	for (unsigned int i = 0 ; i < paiMesh->mNumVertices ; i++) 
	{
		const aiVector3D* pPos      = &(paiMesh->mVertices[i]);
		const aiVector3D* pNormal   = &(paiMesh->mNormals[i]);
		const aiVector3D* pTexCoord = paiMesh->HasTextureCoords(0) ? &(paiMesh->mTextureCoords[0][i]) : &Zero3D;

		Vertex v = {vec3(pPos->x, pPos->y, pPos->z),
			vec3(pNormal->x, pNormal->y, pNormal->z),
			vec2(pTexCoord->x, pTexCoord->y)};

		m_Vertices.push_back(v);
	}

	for (UINT i = 0 ; i < paiMesh->mNumFaces ; i++) 
	{
		const aiFace& Face = paiMesh->mFaces[i];
		assert(Face.mNumIndices == 3);
		m_Indices.push_back(Face.mIndices[0]);
		m_Indices.push_back(Face.mIndices[1]);
		m_Indices.push_back(Face.mIndices[2]);
		nIndices += 3;
	}

	m_Entries[Index].NumIndices = nIndices;
}

void CUnitMesh::Clear()
{

}

bool CUnitMesh::InitD3DObjects()
{
	if (FAILED(CD3DHelper::CreateEffectFromFile(m_pDevice, "..\\Shaders\\Model.fx", m_pEffect)))
	{
		assert(0);
		return false;
	}

	if (FAILED(CD3DHelper::CreateVertexBuffer(m_pDevice, &m_Vertices[0], sizeof(Vertex), m_Vertices.size(), m_pVertexBuffer)))
	{
		
		assert(0);
		return false;
	}

	m_Stride = sizeof(Vertex);

	if (FAILED(CD3DHelper::CreateIndexBuffer(m_pDevice, &m_Indices[0], sizeof(UINT), m_Indices.size(), m_pIndexBuffer)))
	{
		assert(0);
		return false;
	}

	if (FAILED(CD3DHelper::CreateInputLayout(m_pDevice, (Vertex::layout), ARRAYSIZE(Vertex::layout), 
											m_pEffect->GetTechniqueByIndex(0)->GetPassByIndex(0), m_pInputLayout)))
	{
		return false;
	}

	return true;
}

void CUnitMesh::Render(float* Proj, float* View, float* Model, float* World)
{

	HRESULT hr = S_OK;
	for (UINT i = 0; i < m_Entries.size(); i++)
	{
		MeshEntry mEntry = m_Entries[i];
		UINT offset = 0;

		ID3D11DeviceContext* pContext = CD3DHelper::GetImmediateContext(m_pDevice);
		
		//set up all the necessary buffers
		pContext->IASetVertexBuffers(0, 1, &m_pVertexBuffer, &m_Stride, &offset);
		pContext->IASetInputLayout(m_pInputLayout);
		pContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
		pContext->IASetIndexBuffer(m_pIndexBuffer, DXGI_FORMAT_R32_UINT, 0);
		
		m_pEffect->GetVariableByName("proj")->AsMatrix()->SetMatrix(Proj);
		m_pEffect->GetVariableByName("view")->AsMatrix()->SetMatrix(View);
		m_pEffect->GetVariableByName("model")->AsMatrix()->SetMatrix(Model);
		m_pEffect->GetVariableByName("world")->AsMatrix()->SetMatrix(World);
		//for now doing it the messy way
		
		m_pEffect->GetVariableByName("gDiffuseTexture")->AsShaderResource()->SetResource(m_pSRV);

		for (UINT pass = 0; pass < CD3DHelper::GetNumPassesInTechByIndex(m_pEffect, 0); pass++)
		{
			ID3DX11EffectPass* pEffectPass = m_pEffect->GetTechniqueByIndex(0)->GetPassByIndex(pass);
			pEffectPass->Apply(0, pContext);

			pContext->DrawIndexed(mEntry.NumIndices, mEntry.StartIndexLocation, mEntry.BaseVertex);
		}

	}
}

CUnitMesh::CUnitMesh( ID3D11Device* pDevice ) : CAiMesh(pDevice), m_pEffect(NULL), m_pIndexBuffer(NULL)
{

}

CUnitMesh::~CUnitMesh()
{

}

void CUnitMesh::InitMetrials( const aiScene* pScene, const string& Filename )
{
	m_Textures.resize(pScene->mNumMaterials);
	for (UINT i = 0 ; i < m_Textures.size() ; i++) 
	{
		const aiMaterial* pMaterial = pScene->mMaterials[i];

		m_Textures[i] = NULL;

		if (pMaterial->GetTextureCount(aiTextureType_DIFFUSE) > 0) 
		{
			aiString Path;

			if (pMaterial->GetTexture(aiTextureType_DIFFUSE, 0, &Path, NULL, NULL, NULL, NULL, NULL) == AI_SUCCESS) 
			{
				std::string FullPath = ModelsDir + Path.data;
				m_Textures[i] = CD3DHelper::CreateTextureFile(m_pDevice, FullPath);
				ID3D11ShaderResourceView* pRSV = NULL;
				m_pDevice->CreateShaderResourceView(m_Textures[i], NULL, &pRSV);

				m_pSRV = pRSV;

			}
		}
	}
}
